/*-------------------------------------------------------------------------
 *
 * gtm_cmd.c
 *
 *    GTM command module of Postgres-XC configuration and operation tool.
 *
 * Copyright (c) 2013 Postgres-XC Development Group
 *
 * This source code file contains modifications made by THL A29 Limited ("Tencent Modifications").
 * All Tencent Modifications are Copyright (C) 2023 THL A29 Limited.
 *
 *-------------------------------------------------------------------------
 */
/*
 * This module provides various gtm-related pgxc_operation.
 */
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <setjmp.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

#include "pgxc_ctl.h"
#include "do_command.h"
#include "variables.h"
#include "varnames.h"
#include "pgxc_ctl_log.h"
#include "config.h"
#include "do_shell.h"
#include "utils.h"
#include "gtm_cmd.h"
#include "monitor.h"

static char date[MAXTOKEN+1];


/*  ======================================================================================
 *
 * GTM Staff
 *
 * =======================================================================================
 */
/*
 * Init gtm master -----------------------------------------------------------------
 */
cmd_t *prepare_initGtmMaster(bool stop)
{
    cmd_t *cmdInitGtmMaster, *cmdGtmConf, *cmdGxid;
    char date[MAXTOKEN+1];
    FILE *f;
    char **fileList = NULL;
    char remoteDirCheck[MAXPATH * 2 + 128];

    remoteDirCheck[0] = '\0';
    if (!forceInit)
    {
        sprintf(remoteDirCheck, "if [ '$(ls -A %s 2> /dev/null)' ]; then echo 'ERROR: "
                "target directory (%s) exists and not empty. "
                "Skip GTM initilialization'; exit; fi;",
                sval(VAR_gtmMasterDir),
                sval(VAR_gtmMasterDir)
               );
    }

    /* Kill current gtm, bild work directory and run initgtm */
    cmdInitGtmMaster = initCmd(sval(VAR_gtmMasterServer));
    snprintf(newCommand(cmdInitGtmMaster), MAXLINE,
             "%s"
             "[ -f %s/gtm.pid ] && gtm_ctl -D %s -m immediate -Z gtm stop;"
             "rm -rf %s;"
             "mkdir -p %s;"
             "PGXC_CTL_SILENT=1 initgtm -Z gtm -D %s",
             remoteDirCheck,
             sval(VAR_gtmMasterDir),
             sval(VAR_gtmMasterDir),
             sval(VAR_gtmMasterDir), sval(VAR_gtmMasterDir), sval(VAR_gtmMasterDir));

    /* Then prepare gtm.conf file */

    /* Prepare local Stdin */
    appendCmdEl(cmdInitGtmMaster, (cmdGtmConf = initCmd(sval(VAR_gtmMasterServer))));
    if ((f = prepareLocalStdin(newFilename(cmdGtmConf->localStdin), MAXPATH, NULL)) == NULL)
    {
        cleanCmd(cmdInitGtmMaster);
        Free(cmdInitGtmMaster);
        return(NULL);
    }
    fprintf(f, 
            "#===============================================\n"
            "# Added at initialization, %s\n"
            "listen_addresses = '*'\n",
            timeStampString(date, MAXTOKEN));
    if (!is_none(sval(VAR_gtmExtraConfig)))
        AddMember(fileList, sval(VAR_gtmExtraConfig));
    if (!is_none(sval(VAR_gtmMasterSpecificExtraConfig)))
        AddMember(fileList, sval(VAR_gtmMasterSpecificExtraConfig));
    appendFiles(f, fileList);
    CleanArray(fileList);
    fprintf(f,
            "port = %s\n"
            "nodename = '%s'\n"
            "startup = ACT\n"
            "# End of addition\n",
            sval(VAR_gtmMasterPort), sval(VAR_gtmName));
    fclose(f);
    /* other options */
    snprintf(newCommand(cmdGtmConf), MAXLINE,
             "cat >> %s/gtm.conf",
             sval(VAR_gtmMasterDir));

    /* Setup GTM with appropriate GXID value */
    
    appendCmdEl(cmdGtmConf, (cmdGxid = initCmd(sval(VAR_gtmMasterServer))));
     if (stop)
        snprintf(newCommand(cmdGxid), MAXLINE,
             "(gtm -x 2000 -D %s &); sleep 1; gtm_ctl stop -Z gtm -D %s",
             sval(VAR_gtmMasterDir), sval(VAR_gtmMasterDir));
     else
         snprintf(newCommand(cmdGxid), MAXLINE,
              "(gtm -x 2000 -D %s &); sleep 1;",
              sval(VAR_gtmMasterDir));

    return cmdInitGtmMaster;
}
 
int init_gtm_master(bool stop)
{
    int rc = 0;
    cmdList_t *cmdList;
    cmd_t *cmd;

    elog(INFO, "Initialize GTM master\n");
    if (is_none(sval(VAR_gtmMasterServer)))
    {
        elog(INFO, "No GTM master specified, exiting!\n");
        return rc;
    }

    cmdList = initCmdList();

    /* Kill current gtm, build work directory and run initgtm */

    if ((cmd = prepare_initGtmMaster(stop)))
        addCmd(cmdList, cmd);

    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    elog(INFO, "Done.\n");
    return(rc);
}

/*
 * Add gtm master
 *
 */
int add_gtmMaster(char *name, char *host, int port, char *dir)
{
    char port_s[MAXTOKEN+1];
    char date[MAXTOKEN+1];
    FILE *f;
    int    rc;
    char *__port_s__ = NULL;

    if (is_none(name))
    {
        elog(ERROR, "ERROR: Cannot add gtm master with the name \"none\".\n");
        return 1;
    }
    if (is_none(host))
    {
        elog(ERROR, "ERROR: Cannot add gtm master with the name \"none\".\n");
        return 1;
    }
    if (is_none(dir))
    {
        elog(ERROR, "ERROR: Cannot add gtm master with the directory \"none\".\n");
        return 1;
    }
    if (checkSpecificResourceConflict(name, host, port, dir, TRUE))
    {
        elog(ERROR, "ERROR: New specified name:%s, host:%s, port:%d and dir:\"%s\" conflicts with existing node.\n",
             name, host, port, dir);
        return 1;
    }
    assign_sval(VAR_gtmName, Strdup(name));
    assign_sval(VAR_gtmMasterServer, Strdup(host));
    snprintf(port_s, MAXTOKEN, "%d", port);
    __port_s__ = Strdup(port_s);
    assign_sval(VAR_gtmMasterPort, __port_s__);
    assign_sval(VAR_gtmMasterDir, Strdup(dir));
    makeServerList();
    if ((f = fopen(pgxc_ctl_config_path, "a")) == NULL)
    {
        /* Should it be panic? */
        elog(ERROR, "ERROR: cannot open configuration file \"%s\", %s\n", pgxc_ctl_config_path, strerror(errno));
        Free(__port_s__);
        return 1;
    }
    fprintf(f, 
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM master addition\n"
            "#        %s\n", 
            timeStampString(date, MAXTOKEN+1));
    fprintSval(f, VAR_gtmName);
    fprintSval(f, VAR_gtmMasterServer);
    fprintSval(f, VAR_gtmMasterPort);
    fprintSval(f, VAR_gtmMasterDir);
    fprintf(f, "%s","#----End of reconfiguration -------------------------\n");
    fclose(f);
    backup_configuration();
    if ((rc = init_gtm_master(false)) != 0)
    {
        Free(__port_s__);
        return rc;
    }
    Free(__port_s__);
    return(start_gtm_master());
}

/*
 * Add gtm slave: to be used after all the configuration is done.
 *
 * This function only maintains internal configuration, updte configuration file,
 * and make backup if configured.   You should run init_gtm_slave and stat_gtm_slave
 * separately.
 */
int add_gtmSlave(char *name, char *host, int port, char *dir)
{
    char port_s[MAXTOKEN+1];
    char date[MAXTOKEN+1];
    FILE *f;
    int    rc;
    char *__dir__ = NULL;

    if (isVarYes(VAR_gtmSlave))
    {
        elog(ERROR, "ERROR: GTM slave is already configured.\n");
        return 1;
    }
    if (is_none(name))
    {
        elog(ERROR, "ERROR: Cannot add gtm slave with the name \"none\".\n");
        return 1;
    }
    if (is_none(host))
    {
        elog(ERROR, "ERROR: Cannot add gtm slave with the name \"none\".\n");
        return 1;
    }
    if (is_none(dir))
    {
        elog(ERROR, "ERROR: Cannot add gtm slave with the directory \"none\".\n");
        return 1;
    }
    if (checkSpecificResourceConflict(name, host, port, dir, FALSE))
    {
        elog(ERROR, "ERROR: New specified name:%s, host:%s, port:%d and dir:\"%s\" conflicts with existing node.\n",
             name, host, port, dir);
        return 1;
    }
    assign_sval(VAR_gtmSlave, Strdup("y"));
    assign_sval(VAR_gtmSlaveName, Strdup(name));
    assign_sval(VAR_gtmSlaveServer, Strdup(host));
    snprintf(port_s, MAXTOKEN, "%d", port);
    assign_sval(VAR_gtmSlavePort, Strdup(port_s));
    __dir__ = Strdup(dir);
    assign_sval(VAR_gtmSlaveDir, __dir__);
    makeServerList();
    if ((f = fopen(pgxc_ctl_config_path, "a")) == NULL)
    {
        /* Should it be panic? */
        elog(ERROR, "ERROR: cannot open configuration file \"%s\", %s\n", pgxc_ctl_config_path, strerror(errno));
        Free(__dir__);
        return 1;
    }
    fprintf(f, 
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM slave addition\n"
            "#        %s\n", 
            timeStampString(date, MAXTOKEN+1));
    fprintSval(f, VAR_gtmSlave);
    fprintSval(f, VAR_gtmSlaveName);
    fprintSval(f, VAR_gtmSlaveServer);
    fprintSval(f, VAR_gtmSlavePort);
    fprintSval(f, VAR_gtmSlaveDir);
    fprintf(f, "%s","#----End of reconfiguration -------------------------\n");
    fclose(f);
    backup_configuration();
    if ((rc = init_gtm_slave()) != 0)
    {
        Free(__dir__);
        return rc;
    }
    Free(__dir__);
    return(start_gtm_slave());
}

int remove_gtmMaster(bool clean_opt)
{
    FILE *f;

    /* Check if gtm_slave is configured */
    if (!sval(VAR_gtmMasterServer) || is_none(sval(VAR_gtmMasterServer)))
    {
        elog(ERROR, "ERROR: GTM master is not configured.\n");
        return 1;
    }

    /* Check if gtm_master is running and stop if yes */
    if (do_gtm_ping(sval(VAR_gtmMasterServer), atoi(sval(VAR_gtmMasterPort))) == 0)
        stop_gtm_master();

    elog(NOTICE, "Removing gtm master.\n");
    /* Clean */
    if (clean_opt)
        clean_gtm_master();
    /* Reconfigure */
    reset_var(VAR_gtmName);
    assign_sval(VAR_gtmName, Strdup("none"));
    reset_var(VAR_gtmMasterServer);
    assign_sval(VAR_gtmMasterServer, Strdup("none"));
    reset_var(VAR_gtmMasterPort);
    assign_sval(VAR_gtmMasterPort, Strdup("-1"));
    reset_var(VAR_gtmMasterDir);
    assign_sval(VAR_gtmMasterDir, Strdup("none"));
    /* Write the configuration file and bakup it */
    if ((f = fopen(pgxc_ctl_config_path, "a")) == NULL)
    {
        /* Should it be panic? */
        elog(ERROR, "ERROR: cannot open configuration file \"%s\", %s\n", pgxc_ctl_config_path, strerror(errno));
        return 1;
    }
    fprintf(f, 
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM master removal\n"
            "#        %s\n",
            timeStampString(date, MAXTOKEN+1));
    fprintSval(f, VAR_gtmName);
    fprintSval(f, VAR_gtmMasterServer);
    fprintSval(f, VAR_gtmMasterPort);
    fprintSval(f, VAR_gtmMasterDir);
    fprintf(f, "%s", "#----End of reconfiguration -------------------------\n");
    fclose(f);
    backup_configuration();
    elog(NOTICE, "Done.\n");
    return 0;
}

int remove_gtmSlave(bool clean_opt)
{
    FILE *f;

    /* Check if gtm_slave is configured */
    if (!isVarYes(VAR_gtmSlave) || !sval(VAR_gtmSlaveServer) || is_none(sval(VAR_gtmSlaveServer)))
    {
        elog(ERROR, "ERROR: GTM slave is not configured.\n");
        return 1;
    }
    /* Check if gtm_slave is not running */
    if (!do_gtm_ping(sval(VAR_gtmSlaveServer), atoi(sval(VAR_gtmSlavePort))))
    {
        elog(ERROR, "ERROR: GTM slave is now running. Cannot remove it.\n");
        return 1;
    }
    elog(NOTICE, "Removing gtm slave.\n");
    /* Clean */
    if (clean_opt)
        clean_gtm_slave();
    /* Reconfigure */
    reset_var(VAR_gtmSlave);
    assign_sval(VAR_gtmSlave, Strdup("n"));
    reset_var(VAR_gtmSlaveName);
    assign_sval(VAR_gtmSlaveName, Strdup("none"));
    reset_var(VAR_gtmSlaveServer);
    assign_sval(VAR_gtmSlaveServer, Strdup("none"));
    reset_var(VAR_gtmSlavePort);
    assign_sval(VAR_gtmSlavePort, Strdup("-1"));
    reset_var(VAR_gtmSlaveDir);
    assign_sval(VAR_gtmSlaveDir, Strdup("none"));
    /* Write the configuration file and bakup it */
    if ((f = fopen(pgxc_ctl_config_path, "a")) == NULL)
    {
        /* Should it be panic? */
        elog(ERROR, "ERROR: cannot open configuration file \"%s\", %s\n", pgxc_ctl_config_path, strerror(errno));
        return 1;
    }
    fprintf(f, 
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM slave removal\n"
            "#        %s\n",
            timeStampString(date, MAXTOKEN+1));
    fprintSval(f, VAR_gtmSlave);
    fprintSval(f, VAR_gtmSlaveServer);
    fprintSval(f, VAR_gtmSlavePort);
    fprintSval(f, VAR_gtmSlaveDir);
    fprintf(f, "%s", "#----End of reconfiguration -------------------------\n");
    fclose(f);
    backup_configuration();
    elog(NOTICE, "Done.\n");
    return 0;
}


/*
 * Init gtm slave -------------------------------------------------------------
 */

/* 
 * Assumes Gtm Slave is configured.
 * Caller should check this.
 */
cmd_t *prepare_initGtmSlave(void)
{
    char date[MAXTOKEN+1];
    cmd_t *cmdInitGtm, *cmdGtmConf;
    FILE *f;
    char **fileList = NULL;

    if (!isVarYes(VAR_gtmSlave) || (sval(VAR_gtmSlaveServer) == NULL) || is_none(sval(VAR_gtmSlaveServer)))
    {
        elog(ERROR, "ERROR: GTM slave is not configured.\n");
        return(NULL);
    }
    /* Kill current gtm, build work directory and run initgtm */
    cmdInitGtm = initCmd(sval(VAR_gtmSlaveServer));
    snprintf(newCommand(cmdInitGtm), MAXLINE,
             "[ -f %s/gtm.pid ] && gtm_ctl -D %s -m immediate -Z gtm stop;"
             "rm -rf %s;"
             "mkdir -p %s;"
             "PGXC_CTL_SILENT=1 initgtm -Z gtm -D %s",
             sval(VAR_gtmSlaveDir),
             sval(VAR_gtmSlaveDir),
             sval(VAR_gtmSlaveDir), sval(VAR_gtmSlaveDir), sval(VAR_gtmSlaveDir));

    /* Prepare gtm.conf file */

    /* Prepare local Stdin */
    appendCmdEl(cmdInitGtm, (cmdGtmConf = initCmd(sval(VAR_gtmSlaveServer))));
    snprintf(newCommand(cmdGtmConf), MAXLINE,
             "cat >> %s/gtm.conf",
             sval(VAR_gtmSlaveDir));
    if ((f = prepareLocalStdin(newFilename(cmdGtmConf->localStdin), MAXPATH, NULL)) == NULL)
    {
        cleanCmd(cmdInitGtm);
        Free(cmdInitGtm);
        return(NULL);
    }
    fprintf(f, 
            "#===============================================\n"
            "# Added at initialization, %s\n"
            "listen_addresses = '*'\n",
            timeStampString(date, MAXTOKEN+1));
    if (!is_none(sval(VAR_gtmExtraConfig)))
        AddMember(fileList, sval(VAR_gtmExtraConfig));
    if (!is_none(sval(VAR_gtmMasterSpecificExtraConfig)))
        AddMember(fileList, sval(VAR_gtmMasterSpecificExtraConfig));
    appendFiles(f, fileList);
    CleanArray(fileList);
    fprintf(f,
            "port = %s\n"
            "nodename = '%s'\n"
            "startup = STANDBY\n"
            "active_host = '%s'\n"
            "active_port = %d\n"
            "# End of addition\n",
            sval(VAR_gtmSlavePort), sval(VAR_gtmSlaveName),
            sval(VAR_gtmMasterServer), atoi(sval(VAR_gtmMasterPort)));
    fclose(f);
    return (cmdInitGtm);
}

int init_gtm_slave(void)
{
    cmdList_t *cmdList;
    cmd_t *cmdInitGtm;
    int rc;

    elog(INFO, "Initialize GTM slave\n");
    cmdList = initCmdList();
    if ((cmdInitGtm = prepare_initGtmSlave()))
    {
        addCmd(cmdList, cmdInitGtm);
        /* Do all the commands and clean */
        rc = doCmdList(cmdList);
        cleanCmdList(cmdList);
        elog(INFO, "Done.\n");
        return(rc);
    }
    Free(cmdList);
    return 1;
}

/*
 * Start gtm master -----------------------------------------------------
 */
cmd_t *prepare_startGtmMaster(void)
{
    cmd_t *cmdGtmCtl;

    cmdGtmCtl = initCmd(sval(VAR_gtmMasterServer));
    snprintf(newCommand(cmdGtmCtl), MAXLINE, 
             "[ -f %s/gtm.pid ] && gtm_ctl stop -Z gtm -D %s;"
             "rm -f %s/register.node;"
             "gtm_ctl start -Z gtm -D %s",
             sval(VAR_gtmMasterDir),
             sval(VAR_gtmMasterDir),
             sval(VAR_gtmMasterDir),
             sval(VAR_gtmMasterDir));
    return cmdGtmCtl;
}

int start_gtm_master(void)
{
    cmdList_t *cmdList;
    int rc = 0;

    elog(INFO, "Start GTM master\n");
    if (is_none(sval(VAR_gtmMasterServer)))
    {
        elog(INFO, "No GTM master specified, cannot start. Exiting!\n");
        return rc;
    }
    cmdList = initCmdList();
    addCmd(cmdList, prepare_startGtmMaster());
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    return(rc);
}

/*
 * Start gtm slave ----------------------------------------------------
 */
cmd_t *prepare_startGtmSlave(void)
{
    cmd_t *cmdGtmCtl;

    if (!isVarYes(VAR_gtmSlave) || (sval(VAR_gtmSlaveServer) == NULL) || is_none(sval(VAR_gtmSlaveServer)))
    {
        elog(ERROR, "ERROR: GTM slave is not configured.\n");
        return(NULL);
    }
    cmdGtmCtl = initCmd(sval(VAR_gtmSlaveServer));
    snprintf(newCommand(cmdGtmCtl), MAXLINE, 
             "[ -f %s/gtm.pid ] && gtm_ctl stop -Z gtm -D %s;"
             "rm -rf %s/register.node;"
             "gtm_ctl start -Z gtm -D %s",
             sval(VAR_gtmSlaveDir),
             sval(VAR_gtmSlaveDir),
             sval(VAR_gtmSlaveDir),
             sval(VAR_gtmSlaveDir));
    return (cmdGtmCtl);
}

int start_gtm_slave(void)
{
    cmdList_t *cmdList;
    cmd_t *cmd;
    int rc;

    elog(INFO, "Start GTM slave");
    cmdList = initCmdList();
    if ((cmd = prepare_startGtmSlave()))
    {
        addCmd(cmdList, cmd);
        rc = doCmdList(cmdList);
        cleanCmdList(cmdList);
        elog(INFO, "Done.\n");
        return(rc);
    }
    Free(cmdList);
    return 1;
}

/*
 * Stop gtm master ---------------------------------------------------------
 */
cmd_t *prepare_stopGtmMaster(void)
{
    cmd_t *cmdGtmCtl;

    cmdGtmCtl = initCmd(sval(VAR_gtmMasterServer));
    snprintf(newCommand(cmdGtmCtl), MAXLINE,
             "gtm_ctl stop -Z gtm -D %s",
             sval(VAR_gtmMasterDir));
    return(cmdGtmCtl);
}

int stop_gtm_master(void)
{
    cmdList_t *cmdList;
    int rc;

    elog(INFO, "Stop GTM master\n");
    cmdList = initCmdList();
    addCmd(cmdList, prepare_stopGtmMaster());
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    return(rc);
}

/*
 * Stop gtm slave ---------------------------------------------------------------
 */
cmd_t *prepare_stopGtmSlave(void)
{
    cmd_t *cmdGtmCtl;

    if (!isVarYes(VAR_gtmSlave) || (sval(VAR_gtmSlaveServer) == NULL) || is_none(sval(VAR_gtmSlaveServer)))
    {
        elog(ERROR, "ERROR: GTM slave is not configured.\n");
        return(NULL);
    }
    cmdGtmCtl = initCmd(sval(VAR_gtmSlaveServer));
    snprintf(newCommand(cmdGtmCtl), MAXLINE,
             "gtm_ctl stop -Z gtm -D %s",
             sval(VAR_gtmSlaveDir));
    return(cmdGtmCtl);
}

int stop_gtm_slave(void)
{
    cmdList_t *cmdList;
    cmd_t *cmd;
    int rc;

    elog(INFO, "Stop GTM slave\n");
    cmdList = initCmdList();
    if ((cmd = prepare_stopGtmSlave()))
    {
        addCmd(cmdList, cmd);
        rc = doCmdList(cmdList);
        cleanCmdList(cmdList);
        return(rc);
    }
    Free(cmdList);
    return 1;
}

/*
 * Kill gtm master -----------------------------------------------------
 *
 * You should not kill gtm master in this way.   This may discard the latest
 * gtm status.  This is just in case.  You must try to stop gtm master
 * gracefully.
 */
cmd_t *prepare_killGtmMaster(void)
{
    cmd_t *cmdKill;
    pid_t gtmPid;

    cmdKill = initCmd(sval(VAR_gtmMasterServer));
    gtmPid = get_gtm_pid(sval(VAR_gtmMasterServer), sval(VAR_gtmMasterDir));
    if (gtmPid > 0)
        snprintf(newCommand(cmdKill), MAXLINE,
                 "kill -9 %d; rm -rf /tmp/.s.'*'%d'*' %s/gtm.pid",
                 gtmPid, atoi(sval(VAR_gtmMasterPort)), sval(VAR_gtmMasterDir));
    else
    {
        elog(WARNING, "WARNING: pid for gtm master was not found.  Remove socket only.\n");
        snprintf(newCommand(cmdKill), MAXLINE,
                 "rm -rf /tmp/.s.'*'%d'*' %s/gtm.pid",
                 atoi(sval(VAR_gtmMasterPort)), sval(VAR_gtmMasterDir));
    }
    return(cmdKill);
}


int kill_gtm_master(void)
{
    cmdList_t *cmdList;
    cmd_t *cmd_killGtmMaster;
    int rc;

    elog(INFO, "Kill GTM master\n");
    cmdList = initCmdList();
    if ((cmd_killGtmMaster = prepare_killGtmMaster()))
    {
        addCmd(cmdList, cmd_killGtmMaster);
        rc = doCmdList(cmdList);
        cleanCmdList(cmdList);
        return(rc);
    }
    return 1;
}

/*
 * Kill gtm slave --------------------------------------------------------
 *
 * GTM slave has no significant informaion to carry over.  But it is a good
 * habit to stop gtm slave gracefully with stop command.
 */
cmd_t *prepare_killGtmSlave(void)
{
    cmd_t *cmdKill;
    pid_t gtmPid;

    if (!isVarYes(VAR_gtmSlave) || (sval(VAR_gtmSlaveServer) == NULL) || is_none(sval(VAR_gtmSlaveServer)))
    {
        elog(ERROR, "ERROR: GTM slave is not configured.\n");
        return(NULL);
    }
    cmdKill = initCmd(sval(VAR_gtmSlaveServer));
    gtmPid = get_gtm_pid(sval(VAR_gtmSlaveServer), sval(VAR_gtmSlaveDir));
    if (gtmPid > 0)
        snprintf(newCommand(cmdKill), MAXLINE,
                 "kill -9 %d; rm -rf /tmp/.s.'*'%d'*' %s/gtm.pid",
                 gtmPid, atoi(sval(VAR_gtmSlavePort)), sval(VAR_gtmSlaveDir));
    else
    {
        elog(WARNING, "WARNING: pid for gtm slave was not found.  Remove socket only.\n");
        snprintf(newCommand(cmdKill), MAXLINE,
                 "rm -rf /tmp/.s.'*'%d'*' %s/gtm.pid",
                 atoi(sval(VAR_gtmSlavePort)), sval(VAR_gtmSlaveDir));
    }
    return(cmdKill);
}


int kill_gtm_slave(void)
{
    cmdList_t *cmdList;
    cmd_t *cmdKill;
    int rc;

    elog(INFO, "Kill GTM slave\n");
    cmdList = initCmdList();
    if ((cmdKill = prepare_killGtmSlave()))
    {
        addCmd(cmdList, cmdKill);
        rc = doCmdList(cmdList);
        cleanCmdList(cmdList);
        return(rc);
    }
    else
    {
        cleanCmdList(cmdList);
        return 1;
    }
}

/*
 * Failover the gtm ------------------------------------------------------
 */
int failover_gtm(void)
{
    char date[MAXTOKEN+1];
    char *stdIn;
    int rc;
    FILE *f;
    
    elog(INFO, "Failover gtm\n");
    if (!isVarYes(VAR_gtmSlave) || (sval(VAR_gtmSlaveServer) == NULL) || is_none(sval(VAR_gtmSlaveServer)))
    {
        elog(ERROR, "ERROR: GTM slave is not configured. Cannot failover.\n");
        return(1);
    }
    
    if (do_gtm_ping(sval(VAR_gtmSlaveServer), atoi(sval(VAR_gtmSlavePort))) != 0)
    {
        elog(ERROR, "ERROR: GTM slave is not running\n");
        return(1);
    }

    /* Promote the slave */
    elog(NOTICE, "Running \"gtm_ctl promote -Z gtm -D %s\"\n", sval(VAR_gtmSlaveDir));
    rc = doImmediate(sval(VAR_gtmSlaveServer), NULL, 
                     "gtm_ctl promote -Z gtm -D %s", sval(VAR_gtmSlaveDir));
    if (WEXITSTATUS(rc) != 0)
    {
        elog(ERROR, "ERROR: could not promote gtm (host:%s, dir:%s)\n", sval(VAR_gtmSlaveServer), sval(VAR_gtmSlaveDir));
        return 1;
    }

    /* Configure promoted gtm */
    if ((f = prepareLocalStdin(newFilename(stdIn), MAXPATH, NULL)) == NULL)
        return(1);
    fprintf(f,
            "#===================================================\n"
            "# Updated due to GTM failover\n"
            "#        %s\n"
            "startup = ACT\n"
            "#----End of reconfiguration -------------------------\n",
            timeStampString(date, MAXTOKEN+1));
    fclose(f);
    elog(NOTICE, "Updating gtm.conf at %s:%s\n", sval(VAR_gtmSlaveServer), sval(VAR_gtmSlaveDir));
    rc = doImmediate(sval(VAR_gtmSlaveServer), stdIn, "cat >> %s/gtm.conf", sval(VAR_gtmSlaveDir));
    if (WEXITSTATUS(rc) != 0)
    {
        elog(ERROR, "ERROR: could not update gtm.conf (host: %s, dir:%s)\n", sval(VAR_gtmSlaveServer), sval(VAR_gtmSlaveDir));
        Free(stdIn);
        return 1;
    }

    /* Update and backup configuration file */
    if ((f = prepareLocalStdin(stdIn, MAXPATH, NULL)) == NULL)
        return(1);
    fprintf(f,
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM failover\n"
            "#        %s\n"
            "gtmMasterServer=%s\n"
            "gtmMasterPort=%s\n"
            "gtmMasterDir=%s\n"
            "gtmSlave=n\n"
            "gtmSlaveServer=none\n"
            "gtmSlavePort=0\n"
            "gtmSlaveDir=none\n"
            "#----End of reconfiguration -------------------------\n",
            timeStampString(date, MAXTOKEN+1),
            sval(VAR_gtmSlaveServer),
            sval(VAR_gtmSlavePort),
            sval(VAR_gtmSlaveDir));
    fclose(f);
    rc = doImmediate(NULL, stdIn, "cat >> %s", pgxc_ctl_config_path);
    if (WEXITSTATUS(rc) != 0)
    {
        elog(ERROR, "ERROR: could not update gtm.conf (host: %s, dir:%s)\n", sval(VAR_gtmSlaveServer), sval(VAR_gtmSlaveDir));
        return 1;
    }
    freeAndReset(stdIn);
    backup_configuration();

    /* Reconfigure myself */
    assign_val(VAR_gtmMasterServer, VAR_gtmSlaveServer); reset_var(VAR_gtmSlaveServer);
    assign_val(VAR_gtmMasterPort, VAR_gtmSlavePort); reset_var(VAR_gtmSlavePort);
    assign_val(VAR_gtmMasterDir, VAR_gtmSlaveDir); reset_var(VAR_gtmSlaveDir);
    assign_sval(VAR_gtmSlaveServer, "none");
    assign_sval(VAR_gtmSlavePort, "0");
    assign_sval(VAR_gtmSlaveDir, "none");
    assign_sval(VAR_gtmSlave, "n");

    return 0;
}

/*
 * Clean gtm master resources -- directory and socket --------------------------
 */
cmd_t *prepare_cleanGtmMaster(void)
{
    cmd_t *cmd;

    /* Remote work dir and clean the socket */
    cmd = initCmd(sval(VAR_gtmMasterServer));
    snprintf(newCommand(cmd), MAXLINE, 
             "rm -rf %s; mkdir -p %s; chmod 0700 %s;rm -f /tmp/.s.*%d*",
             sval(VAR_gtmMasterDir), sval(VAR_gtmMasterDir), sval(VAR_gtmMasterDir),
             atoi(VAR_gtmMasterPort));
    return cmd;
}

int clean_gtm_master(void)
{
    cmdList_t *cmdList;
    int rc;

    elog(INFO, "Clearing gtm master directory and socket.\n");
    cmdList = initCmdList();
    addCmd(cmdList, prepare_cleanGtmMaster());
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    return(rc);
}

/*
 * Clean gtm master resources -- direcotry and socket --------------------------
 */
/*
 * Null will be retruend if gtm slave is not configured.
 * Be careful.   If you configure gtm slave and gtm master on a same server,
 * bott slave amd master process will be killed.
 */
cmd_t *prepare_cleanGtmSlave(void)
{
    cmd_t *cmd;
    
    if (!isVarYes(VAR_gtmSlave) || is_none(VAR_gtmSlaveServer))
        return(NULL);
    cmd = initCmd(sval(VAR_gtmSlaveServer));
    snprintf(newCommand(cmd), MAXLINE,
             "rm -rf %s; mkdir -p %s; chmod 0700 %s;rm -f /tmp/.s.*%d*",
             sval(VAR_gtmSlaveDir), sval(VAR_gtmSlaveDir), sval(VAR_gtmMasterDir),
             atoi(VAR_gtmSlavePort));
    return cmd;
}

int clean_gtm_slave(void)
{
    cmdList_t *cmdList;
    int rc;

    elog(NOTICE, "Clearing gtm slave resources.\n");
    if (!isVarYes(VAR_gtmSlave) || is_none(VAR_gtmSlaveServer))
    {
        elog(ERROR, "ERROR: gtm slave is not configured.\n");
        return 1;
    }
    cmdList = initCmdList();
    addCmd(cmdList, prepare_cleanGtmSlave());
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    elog(NOTICE, "Done.\n");
    return(rc);
}

/*
 * ==================================================================================
 *
 * Gtm Proxy Staff
 *
 * ==================================================================================
 */

/*
 * Add gtm proxy: to be used after all the configuration is done.
 *
 * This function only maintains internal configuration, updte configuration file,
 * and make backup if configured.   You should run init and start it separately.
 */
int add_gtmProxy(char *name, char *host, int port, char *dir)
{
    char port_s[MAXTOKEN+1];
    char date[MAXTOKEN+1];
    FILE *f;
    char **nodelist = NULL;
    int rc;

    if (is_none(host))
    {
        elog(ERROR, "ERROR: Cannot add gtm proxy with the name \"none\".\n");
        return 1;
    }
    if (is_none(dir))
    {
        elog(ERROR, "ERROR: Cannot add gtm proxy with the directory \"none\".\n");
        return 1;
    }
    if (checkSpecificResourceConflict(name, host, port, dir, TRUE))
    {
        elog(ERROR, "ERROR: New specified name:%s, host:%s, port:%d and dir:\"%s\" conflicts with existing node.\n",
             name, host, port, dir);
        return 1;
    }
    if (!isVarYes(VAR_gtmProxy))
    {
        assign_sval(VAR_gtmProxy, Strdup("y"));
        reset_var(VAR_gtmProxyNames);
        reset_var(VAR_gtmProxyServers);
        reset_var(VAR_gtmProxyPorts);
        reset_var(VAR_gtmProxyDirs);
        reset_var(VAR_gtmPxySpecificExtraConfig);
        reset_var(VAR_gtmPxyExtraConfig);
    }
    add_val(find_var(VAR_gtmProxyNames), Strdup(name));
    add_val(find_var(VAR_gtmProxyServers), Strdup(host));
    snprintf(port_s, MAXTOKEN, "%d", port);
    add_val(find_var(VAR_gtmProxyPorts), Strdup(port_s));
    add_val(find_var(VAR_gtmProxyDirs), Strdup(dir));
    add_val(find_var(VAR_gtmPxySpecificExtraConfig), Strdup("none"));
    makeServerList();
    if ((f = fopen(pgxc_ctl_config_path, "a")) == NULL)
    {
        /* Should it be panic? */
        elog(ERROR, "ERROR: cannot open configuration file \"%s\", %s\n", pgxc_ctl_config_path, strerror(errno));
        return 1;
    }
    fprintf(f, 
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM proxy (%s) addition\n"
            "#        %s\n",
            name,
            timeStampString(date, MAXTOKEN+1));
    fprintSval(f, VAR_gtmProxy);
    fprintAval(f, VAR_gtmProxyNames);
    fprintAval(f, VAR_gtmProxyServers);
    fprintAval(f, VAR_gtmProxyPorts);
    fprintAval(f, VAR_gtmProxyDirs);
    fprintAval(f, VAR_gtmPxySpecificExtraConfig);
    fprintf(f, "%s", "#----End of reconfiguration -------------------------\n");
    fclose(f);
    AddMember(nodelist, name);
    init_gtm_proxy(nodelist);
    rc = start_gtm_proxy(nodelist);
    CleanArray(nodelist);
    return rc;
}

int remove_gtmProxy(char *name, bool clean_opt)
{
    FILE *f;
    int idx;

    /* Check if gtm proxy exists */
    if ((idx = gtmProxyIdx(name)) < 0)
    {
        elog(ERROR, "ERROR: %s is not a gtm proxy.\n", name);
        return 1;
    }
    /* Check if it is in use */
    if (ifExists(VAR_coordMasterServers, aval(VAR_gtmProxyServers)[idx]) ||
        ifExists(VAR_coordSlaveServers, aval(VAR_gtmProxyServers)[idx]) ||
        ifExists(VAR_datanodeMasterServers, aval(VAR_gtmProxyServers)[idx]) ||
        ifExists(VAR_datanodeSlaveServers, aval(VAR_gtmProxyServers)[idx]))
    {
        elog(ERROR, "ERROR: GTM Proxy %s is in use\n", name);
        return 1;
    }
    elog(NOTICE, "NOTICE: removing gtm_proxy %s\n", name);
    /* Clean */
    if (clean_opt)
    {
        char **nodelist = NULL;

        elog(NOTICE, "NOTICE: cleaning target resources.\n");
        AddMember(nodelist, name);
        clean_gtm_proxy(nodelist);
        CleanArray(nodelist);
    }
    /* Reconfigure */
    var_assign(&aval(VAR_gtmProxyNames)[idx], Strdup("none"));
    var_assign(&aval(VAR_gtmProxyServers)[idx], Strdup("none"));
    var_assign(&aval(VAR_gtmProxyPorts)[idx], Strdup("-1"));
    var_assign(&aval(VAR_gtmProxyDirs)[idx], Strdup("none"));
    handle_no_slaves();
    makeServerList();
    /* Update configuration file and backup it */
    if ((f = fopen(pgxc_ctl_config_path, "a")) == NULL)
    {
        /* Should it be panic? */
        elog(ERROR, "ERROR: cannot open configuration file \"%s\", %s\n", pgxc_ctl_config_path, strerror(errno));
        return 1;
    }
    fprintf(f, 
            "#===================================================\n"
            "# pgxc configuration file updated due to GTM proxy addition\n"
            "#        %s\n"
            "%s=%s\n"                    /* gtmProxy */
            "%s=( %s )\n"                /* gtmProxyNames */
            "%s=( %s )\n"                /* gtmProxyServers */
            "%s=( %s )\n"                /* gtmProxyPorts */
            "%s=( %s )\n"                /* gtmProxyDirs */
            "#----End of reconfiguration -------------------------\n",
            timeStampString(date, MAXTOKEN+1),
            VAR_gtmProxy, sval(VAR_gtmProxy),
            VAR_gtmProxyNames, listValue(VAR_gtmProxyNames),
            VAR_gtmProxyServers, listValue(VAR_gtmProxyServers),
            VAR_gtmProxyPorts, listValue(VAR_gtmProxyPorts),
            VAR_gtmProxyDirs, listValue(VAR_gtmProxyDirs));
    fclose(f);
    backup_configuration();
    elog(NOTICE, "Done.\n");
    return 0;
}

/* 
 * Does not check if node name is valid.
 */

cmd_t *prepare_initGtmProxy(char *nodeName)
{
    cmd_t *cmdInitGtm, *cmdGtmProxyConf;
    int idx;
    FILE *f;
    char timestamp[MAXTOKEN+1];
    char **fileList = NULL;
    char remoteDirCheck[MAXPATH * 2 + 128];

    if ((idx = gtmProxyIdx(nodeName)) < 0)
    {
        elog(ERROR, "ERROR: Specified name %s is not GTM Proxy configuration.\n", nodeName);
        return NULL;
    }

    remoteDirCheck[0] = '\0';
    if (!forceInit)
    {
        sprintf(remoteDirCheck, "if [ '$(ls -A %s 2> /dev/null)' ]; then echo 'ERROR: "
                "target directory (%s) exists and not empty. "
                "Skip GTM proxy initilialization'; exit; fi;",
                aval(VAR_gtmProxyDirs)[idx],
                aval(VAR_gtmProxyDirs)[idx]);
    }

    /* Build directory and run initgtm */
    cmdInitGtm = initCmd(aval(VAR_gtmProxyServers)[idx]);
    snprintf(newCommand(cmdInitGtm), MAXLINE,
             "%s"
             "[ -f %s/gtm_proxy.pid ] && gtm_ctl -D %s -m immediate -Z gtm_proxy stop;"
             "rm -rf %s;"
             "mkdir -p %s;"
             "PGXC_CTL_SILENT=1 initgtm -Z gtm_proxy -D %s",
             remoteDirCheck,
             aval(VAR_gtmProxyDirs)[idx],
             aval(VAR_gtmProxyDirs)[idx],
             aval(VAR_gtmProxyDirs)[idx],
             aval(VAR_gtmProxyDirs)[idx],
             aval(VAR_gtmProxyDirs)[idx]);

    /* Configure gtm_proxy.conf */
    appendCmdEl(cmdInitGtm, (cmdGtmProxyConf = initCmd(aval(VAR_gtmProxyServers)[idx])));
    if ((f = prepareLocalStdin(newFilename(cmdGtmProxyConf->localStdin), MAXPATH, NULL)) == NULL)
    {
        cleanCmd(cmdInitGtm);
        Free(cmdInitGtm);
        return NULL;
    }
    fprintf(f,
            "#===========================\n"
            "# Added at initialization, %s\n"
            "nodename = '%s'\n"
            "listen_addresses = '*'\n",
            timeStampString(timestamp, MAXTOKEN),
            aval(VAR_gtmProxyNames)[idx]);

    fprintf(f,
            "port = %s\n"
            "gtm_host = '%s'\n"
            "gtm_port = %s\n"
            "worker_threads = 1\n"
            "gtm_connect_retry_interval = 1\n"
            "# End of addition\n",
            aval(VAR_gtmProxyPorts)[idx],
            sval(VAR_gtmMasterServer),
            sval(VAR_gtmMasterPort));

    if (!is_none(sval(VAR_gtmPxyExtraConfig)))
        AddMember(fileList, sval(VAR_gtmPxyExtraConfig));
    if (!is_none(sval(VAR_gtmPxySpecificExtraConfig)))
        AddMember(fileList, sval(VAR_gtmPxySpecificExtraConfig));
    appendFiles(f, fileList);
    CleanArray(fileList);

    fclose(f);
    snprintf(newCommand(cmdGtmProxyConf), MAXLINE,
             "cat >> %s/gtm_proxy.conf", aval(VAR_gtmProxyDirs)[idx]);
    return(cmdInitGtm);
}

/*
 * Initialize gtm proxy -------------------------------------------------------
 */
int init_gtm_proxy(char **nodeList)
{
    char **actualNodeList;
    int ii;
    int rc;
    cmdList_t *cmdList;
    cmd_t *cmdInitGtmPxy;

    if (!isVarYes(VAR_gtmProxy))
    {
        elog(ERROR, "ERROR: GTM Proxy is not configured.\n");
        return 1;
    }
    actualNodeList = makeActualNodeList(nodeList);
    /* Init and run initgtm */
    cmdList = initCmdList();
    for (ii = 0; actualNodeList[ii]; ii++)
    {
        elog(NOTICE, "Initializing gtm proxy %s.\n", actualNodeList[ii]);
        if ((cmdInitGtmPxy = prepare_initGtmProxy(actualNodeList[ii])))
            addCmd(cmdList, cmdInitGtmPxy);
        else
            elog(WARNING, "WARNING: %s is not a gtm proxy.\n", actualNodeList[ii]);
    }
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    CleanArray(actualNodeList);
    elog(NOTICE, "Done.\n");
    return(rc);
}


int init_gtm_proxy_all(void)
{
    elog(NOTICE, "Initialize all the gtm proxies.\n");
    if (!isVarYes(VAR_gtmProxy))
    {
        elog(ERROR, "ERROR: GTM Proxy is not configured.\n");
        return(1);
    }
    return(init_gtm_proxy(aval(VAR_gtmProxyNames)));
}

/*
 * Start gtm proxy -----------------------------------------------------------
 */
cmd_t *prepare_startGtmProxy(char *nodeName)
{
    cmd_t *cmd;
    int idx;

    if ((idx = gtmProxyIdx(nodeName)) < 0)
    {
        elog(ERROR, "ERROR: Specified name %s is not GTM Proxy\n", nodeName);
        return(NULL);
    }
    cmd = initCmd(aval(VAR_gtmProxyServers)[idx]);
    snprintf(newCommand(cmd), MAXLINE,
             "[ -f %s/gtm_proxy.pid ] && gtm_ctl -D %s -m immediate -Z gtm_proxy stop;"
             "gtm_ctl start -Z gtm_proxy -D %s",
             aval(VAR_gtmProxyDirs)[idx],
             aval(VAR_gtmProxyDirs)[idx],
             aval(VAR_gtmProxyDirs)[idx]);
    return(cmd);
}

int start_gtm_proxy(char **nodeList)
{
    char **actualNodeList;
    int ii;
    int rc;
    cmdList_t *cmdList;

    if (!isVarYes(VAR_gtmProxy))
    {
        elog(ERROR, "ERROR: GTM Proxy is not configured.\n");
        return(1);
    }
    actualNodeList = makeActualNodeList(nodeList);
    /* Init and run initgtm */
    cmdList = initCmdList();
    for (ii = 0; actualNodeList[ii]; ii++)
    {
        cmd_t *cmd;
        elog(NOTICE, "Starting gtm proxy %s.\n", actualNodeList[ii]);
        if ((cmd = prepare_startGtmProxy(actualNodeList[ii])))
            addCmd(cmdList, cmd);
        else
            elog(WARNING, "WARNING: %s is not a gtm proxy.\n", actualNodeList[ii]);
    }
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    CleanArray(actualNodeList);
    elog(NOTICE, "Done.\n");
    return(rc);
}

int start_gtm_proxy_all(void)
{
    elog(NOTICE, "Starting all the gtm proxies.\n");
    return(start_gtm_proxy(aval(VAR_gtmProxyNames)));
}

/*
 * Stop gtm proxy -------------------------------------------------------------
 */
cmd_t *prepare_stopGtmProxy(char *nodeName)
{
    cmd_t *cmd;
    int idx;

    if ((idx = gtmProxyIdx(nodeName)) < 0)
    {
        elog(ERROR, "ERROR: Specified name %s is not GTM Proxy\n", nodeName);
        return NULL;
    }
    cmd = initCmd(aval(VAR_gtmProxyServers)[idx]);
    snprintf(newCommand(cmd), MAXLINE,
             "gtm_ctl stop -Z gtm_proxy -D %s",
             aval(VAR_gtmProxyDirs)[idx]);
    return(cmd);
}


int stop_gtm_proxy(char **nodeList)
{
    char **actualNodeList;
    int ii;
    int rc;
    cmdList_t *cmdList;

    if (!isVarYes(VAR_gtmProxy))
    {
        elog(ERROR, "ERROR: GTM Proxy is not configured.\n");
        return(1);
    }
    actualNodeList = makeActualNodeList(nodeList);
    /* Init and run initgtm */
    cmdList = initCmdList();
    for (ii = 0; actualNodeList[ii]; ii++)
    {
        cmd_t *cmd;

        elog(NOTICE, "Stopping gtm proxy %s.\n", actualNodeList[ii]);
        if ((cmd = prepare_stopGtmProxy(actualNodeList[ii])))
            addCmd(cmdList, cmd);
        else
            elog(WARNING, "WARNING: %s is not a gtm proxy.\n", actualNodeList[ii]);
    }
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    CleanArray(actualNodeList);
    elog(NOTICE, "Done.\n");
    return(rc);
}

int stop_gtm_proxy_all(void)
{
    elog(NOTICE, "Stopping all the gtm proxies.\n");
    return(stop_gtm_proxy(aval(VAR_gtmProxyNames)));
}

/*
 * Kill gtm proxy -------------------------------------------------------------------
 *
 * Although gtm proxy does not have significant resources to carry over to the next
 * run, it is a good habit to stop gtm proxy with stop command gracefully.
 */
cmd_t *prepare_killGtmProxy(char *nodeName)
{
    cmd_t *cmd;
    int idx;
    pid_t gtmPxyPid;

    if ((idx = gtmProxyIdx(nodeName)) < 0)
    {
        elog(ERROR, "ERROR: Specified name %s is not GTM Proxy\n", nodeName);
        return NULL;
    }
    cmd = initCmd(aval(VAR_gtmProxyServers)[idx]);
    gtmPxyPid = get_gtmProxy_pid(aval(VAR_gtmProxyServers)[idx], aval(VAR_gtmProxyDirs)[idx]);
    if (gtmPxyPid > 0)
        snprintf(newCommand(cmd), MAXLINE,
                 "kill -9 %d; rm -rf /tmp/.s.'*'%d'*' %s/gtm_proxy.pid",
                 gtmPxyPid, atoi(aval(VAR_gtmProxyPorts)[idx]), aval(VAR_gtmProxyDirs)[idx]);
    else
    {
        elog(WARNING, "WARNING: pid for gtm proxy \"%s\" was not found.  Remove socket only.\n", nodeName);
        snprintf(newCommand(cmd), MAXLINE,
                 "rm -rf /tmp/.s.'*'%d'*' %s/gtm_proxy.pid",
                 atoi(aval(VAR_gtmProxyPorts)[idx]), aval(VAR_gtmProxyDirs)[idx]);
    }
    return(cmd);
}

int kill_gtm_proxy(char **nodeList)
{
    char **actualNodeList;
    int ii;
    int rc;
    cmdList_t *cmdList;

    if (!isVarYes(VAR_gtmProxy))
    {
        elog(ERROR, "ERROR: GTM Proxy is not configured.\n");
        return(1);
    }
    actualNodeList = makeActualNodeList(nodeList);
    /* Init and run initgtm */
    cmdList = initCmdList();
    for (ii = 0; actualNodeList[ii]; ii++)
    {
        cmd_t *cmd;

        elog(NOTICE, "Killing process of gtm proxy %s.\n", actualNodeList[ii]);
        if ((cmd = prepare_killGtmProxy(actualNodeList[ii])))
            addCmd(cmdList, cmd);
        else
            elog(WARNING, "WARNING: %s is not a gtm proxy.\n", actualNodeList[ii]);
    }
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    CleanArray(actualNodeList);
    return(rc);
}

int kill_gtm_proxy_all(void)
{
    elog(NOTICE, "Killing all the gtm proxy processes.\n");
    return(kill_gtm_proxy(aval(VAR_gtmProxyNames)));
}

/*
 * Reconnect to the current GTM master --------------------------------------------------
 *
 * When failed over, the current Master must have been updated.
 * Remember to update gtm_proxy configuration file so that it 
 * connects to the new master at the next start.
 * Please note that we assume GTM has already been failed over.
 * First argument is gtm_proxy nodename
 */
cmd_t *prepare_reconnectGtmProxy(char *nodeName)
{
    cmd_t *cmdGtmCtl, *cmdGtmProxyConf;
    int idx;
    FILE *f;
    char date[MAXTOKEN+1];

    if ((idx = gtmProxyIdx(nodeName)) < 0)
    {
        elog(ERROR, "ERROR: Specified name %s is not GTM Proxy\n", nodeName);
        return(NULL);
    }

    /* gtm_ctl reconnect */
    cmdGtmCtl = initCmd(aval(VAR_gtmProxyServers)[idx]);
    snprintf(newCommand(cmdGtmCtl), MAXLINE,
             "gtm_ctl reconnect -Z gtm_proxy -D %s -o \\\"-s %s -t %s\\\"",
             aval(VAR_gtmProxyDirs)[idx], sval(VAR_gtmMasterServer), sval(VAR_gtmMasterPort));

    /* gtm_proxy.conf */
    appendCmdEl(cmdGtmCtl, (cmdGtmProxyConf = initCmd(aval(VAR_gtmProxyServers)[idx])));
    if ((f = prepareLocalStdin(newFilename(cmdGtmProxyConf->localStdin), MAXPATH, NULL)) == NULL)
    {
        cleanCmd(cmdGtmCtl);
        Free(cmdGtmCtl);
        return(NULL);
    }
    fprintf(f, 
            "#===================================================\n"
            "# Updated due to GTM Proxy reconnect\n"
            "#        %s\n"
            "gtm_host = '%s'\n"
            "gtm_port = %s\n"
            "#----End of reconfiguration -------------------------\n",
            timeStampString(date, MAXTOKEN),
            sval(VAR_gtmMasterServer),
            sval(VAR_gtmMasterPort));
    fclose(f);
    snprintf(newCommand(cmdGtmProxyConf), MAXLINE,
             "cat >> %s/gtm_proxy.conf", aval(VAR_gtmProxyDirs)[idx]);
    return(cmdGtmCtl);
}


int reconnect_gtm_proxy(char **nodeList)
{
    char **actualNodeList;
    int ii;
    int rc;
    cmdList_t *cmdList;

    if (!isVarYes(VAR_gtmProxy))
    {
        elog(ERROR, "GTM Proxy is not configured.\n");
        return(1);
    }
    actualNodeList = makeActualNodeList(nodeList);
    /* Init and run initgtm */
    cmdList = initCmdList();
    for (ii = 0; actualNodeList[ii]; ii++)
    {
        cmd_t *cmd;

        elog(NOTICE, "Reconnecting gtm proxy %s.\n", actualNodeList[ii]);
        if((cmd = prepare_reconnectGtmProxy(actualNodeList[ii])))
            addCmd(cmdList, cmd);
        else
            elog(WARNING, "WARNING: %s is not a gtm proxy.\n", actualNodeList[ii]);
    }
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    CleanArray(actualNodeList);
    elog(NOTICE, "Done.\n");
    return(rc);
}

int reconnect_gtm_proxy_all(void)
{
    elog(NOTICE, "Reconnecting all the gtm proxies to the new one.\n");
    return(reconnect_gtm_proxy(aval(VAR_gtmProxyNames)));
}

/*
 * Cleanup -- nodeName must be valid.   Instead, NULL will bereturned.
 */
cmd_t *prepare_cleanGtmProxy(char *nodeName)
{
    cmd_t *cmd;
    int   idx;
    
    if ((idx = gtmProxyIdx(nodeName)) < 0)
        return NULL;
    cmd = initCmd(aval(VAR_gtmProxyServers)[idx]);
    snprintf(newCommand(cmd), MAXLINE,
             "rm -rf %s; mkdir -p %s; chmod 0700 %s;rm -f /tmp/.s.*%d*",
             aval(VAR_gtmProxyDirs)[idx], aval(VAR_gtmProxyDirs)[idx], aval(VAR_gtmProxyDirs)[idx], 
             atoi(aval(VAR_gtmProxyPorts)[idx]));
    return cmd;
}

int clean_gtm_proxy(char **nodeList)
{
    char **actualNodeList;
    cmdList_t *cmdList;
    cmd_t *cmd;
    int ii;
    int rc;

    actualNodeList = makeActualNodeList(nodeList);
    cmdList = initCmdList();
    for (ii = 0; actualNodeList[ii]; ii++)
    {
        elog(NOTICE, "Clearing resources for gtm_proxy %s.\n", actualNodeList[ii]);
        if ((cmd = prepare_cleanGtmProxy(actualNodeList[ii])))
            addCmd(cmdList, cmd);
        else
            elog(WARNING, "%s is not a gtm proxy.\n", actualNodeList[ii]);
    }
    rc = doCmdList(cmdList);
    cleanCmdList(cmdList);
    CleanArray(actualNodeList);
    elog(NOTICE, "Done.\n");
    return(rc);
}

int clean_gtm_proxy_all(void)
{
    elog(NOTICE, "Clearing all the gtm_proxy resources.\n");
    return(clean_gtm_proxy(aval(VAR_gtmProxyNames)));
}

/*
 * configuration --------------------------------------------------------------------
 */
int show_config_gtmMaster(int flag, char *hostname)
{
    char lineBuf[MAXLINE+1];
    char editBuf[MAXPATH+1];

    lineBuf[0] = 0;
    if (flag)
        strncat(lineBuf, "GTM Master: ", MAXLINE);
    if (hostname)
    {
        snprintf(editBuf, MAXPATH, "host: %s", hostname);
        strncat(lineBuf, editBuf, MAXLINE);
    }
    if (flag || hostname)
        strncat(lineBuf, "\n", MAXLINE);
    lockLogFile();
    if (lineBuf[0])
        elog(NOTICE, "%s", lineBuf);
    print_simple_node_info(sval(VAR_gtmName), sval(VAR_gtmMasterPort), sval(VAR_gtmMasterDir),
                           sval(VAR_gtmExtraConfig), sval(VAR_gtmMasterSpecificExtraConfig));
    unlockLogFile();
    return 0;
}

int show_config_gtmSlave(int flag, char *hostname)
{
    char lineBuf[MAXLINE+1];
    char editBuf[MAXPATH+1];

    if (!isVarYes(VAR_gtmSlave) || is_none(VAR_gtmSlaveServer))
    {
        elog(ERROR, "ERROR: gtm slave is not configured.\n");
        return 0;
    }
    lineBuf[0] = 0;
    if (flag)
        strncat(lineBuf, "GTM Slave: ", MAXLINE);
    if (hostname)
    {
        snprintf(editBuf, MAXPATH, "host: %s", hostname);
        strncat(lineBuf, editBuf, MAXLINE);
    }
    if (flag || hostname)
        strncat(lineBuf, "\n", MAXLINE);
    lockLogFile();
    elog(NOTICE, "%s", lineBuf);
    print_simple_node_info(sval(VAR_gtmSlaveName), sval(VAR_gtmSlavePort), sval(VAR_gtmSlaveDir),
                           sval(VAR_gtmExtraConfig), sval(VAR_gtmSlaveSpecificExtraConfig));
    unlockLogFile();
    return 0;
}

int show_config_gtmProxies(char **nameList)
{
    int ii;

    lockLogFile();
    for(ii = 0; nameList[ii]; ii++)
        show_config_gtmProxy(TRUE, ii, aval(VAR_gtmProxyServers)[ii]);
    unlockLogFile();
    return 0;
}

int show_config_gtmProxy(int flag, int idx, char *hostname)
{
    char lineBuf[MAXLINE+1];
    char editBuf[MAXPATH+1];

    lineBuf[0] = 0;
    if (flag)
        strncat(lineBuf, "GTM Proxy: ", MAXLINE);
    if (hostname)
    {
        snprintf(editBuf, MAXPATH, "host: %s", hostname);
        strncat(lineBuf, editBuf, MAXLINE);
    }
    if (flag || hostname)
        strncat(lineBuf, "\n", MAXLINE);
    lockLogFile();
    if (lineBuf[0])
        elog(NOTICE, "%s", lineBuf);
    print_simple_node_info(aval(VAR_gtmProxyNames)[idx], aval(VAR_gtmProxyPorts)[idx],
                           aval(VAR_gtmProxyDirs)[idx], sval(VAR_gtmPxyExtraConfig),
                           aval(VAR_gtmPxySpecificExtraConfig)[idx]);
    unlockLogFile();
    return 0;
}
